﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Threading;

namespace Sonce.Controls
{
    [TemplateVisualState(GroupName = "CommonStates", Name = "Normal")]
    [TemplateVisualState(GroupName = "CommonStates", Name = "MouseOver")]
    [TemplateVisualState(GroupName = "CommonStates", Name = "Pressed")]
    [TemplateVisualState(GroupName = "CommonStates", Name = "Disabled")]
    [TemplatePart(Name = "Header", Type = typeof(MenuItemHeader))]
    public partial class MenuItem : ItemsControl
    {
        #region Members
        private Menu parentMenu;
        private MenuItem parentMenuItem;
        private MenuItem parentMainMenuItem;
        #endregion

        #region Constructor
        public MenuItem()
            : base()
        {
            this.DefaultStyleKey = typeof(MenuItem);
            this.LayoutUpdated += new EventHandler(MenuItem_LayoutUpdated);
        }

        public MenuItem(string text)
            : this()
        {
            this.Header = text;
        }
        #endregion

        #region Properties
        public bool HasChild //CanExpand
        {
            get
            {
                return (base.Items != null && base.Items.Count > 0);
            }
        }

        public new bool IsEnabled
        {
            get
            {
                return base.IsEnabled;
            }
            set
            {
                base.IsEnabled = value;
                //if (value)
                //{
                //    EnablePopupProvider();
                //    VisualStateManager.GoToState(this, "Normal", true);
                //}
                //else
                //{
                //    DisablePopupProvider();
                //    VisualStateManager.GoToState(this, "Disabled", true);
                //}
            }
        }

        public bool IsExpanded //IsSubmenuOpen
        {
            get
            {
                return (bool)GetValue(IsExpandedProperty);
            }
            set
            {
                SetValue(IsExpandedProperty, value);
                SetValue(ContextMenuVisibilityProperty, value ? Visibility.Visible : Visibility.Collapsed);

                if (popup != null)
                {
                    popup.IsOpen = value;
                }
            }
        }

        /// <summary>
        /// Gets the value whether the current MenuItem is presented on the Menu bar.
        /// </summary>
        public bool IsMainMenuItem
        {
            get
            {
                //throws E_FAIL COM exception
                //return this.Parent is Menu; //TODO: What if parent is StackPanel?
                return false;
            }
        }

        public object Header
        {
            get
            {
                return GetValue(HeaderProperty);
            }
            set
            {
                SetValue(HeaderProperty, value);
            }
        }

        public Menu ParentMenu
        {
            get
            {
                if (this.parentMenu == null)
                {
                    object result = Helper.FindParent<Menu>(this);
                    if (result != null && result is Menu)
                    {
                        this.parentMenu = result as Menu;
                    }
                }
                return this.parentMenu;
            }
            set
            {
                this.parentMenu = value;
            }
        }

        public MenuItem ParentMenuItem
        {
            get
            {
                if (this.parentMenuItem == null)
                {
                    object result = Helper.FindParent<MenuItem>(this);
                    if (result != null && result is MenuItem)
                    {
                        this.parentMenuItem = result as MenuItem;
                    }
                }
                return this.parentMenuItem;
            }
            set
            {
                this.parentMenuItem = value;
            }
        }

        public MenuItem ParentMainMenuItem
        {
            get
            {
                if (this.parentMainMenuItem == null)
                {
                    object result = FindMainMenuItem(this);
                    if (result != null && result is MenuItem)
                    {
                        this.parentMainMenuItem = result as MenuItem;
                    }
                }
                return this.parentMainMenuItem;
            }
            set
            {
                this.parentMainMenuItem = value;
            }
        }

        public Visibility ContextMenuVisibility
        {
            get
            {
                return (Visibility)GetValue(ContextMenuVisibilityProperty);
            }
            set
            {
                SetValue(ContextMenuVisibilityProperty, value);
            }
        }

        public Visibility ArrowVisibility
        {
            get
            {
                return (Visibility)GetValue(ArrowVisibilityProperty);
            }
            set
            {
                SetValue(ArrowVisibilityProperty, value);
            }
        }

        public double ArrowContainerWidth
        {
            get
            {
                return (double)GetValue(ArrowContainerWidthProperty);
            }
            set
            {
                SetValue(ArrowContainerWidthProperty, value);
            }
        }

        public double PopupOffset
        {
            get
            {
                return (double)GetValue(PopupOffsetProperty);
            }
            set
            {
                SetValue(PopupOffsetProperty, value);

            }
        }

        public DataTemplate HeaderTemplate
        {
            get
            {
                return (DataTemplate)GetValue(HeaderTemplateProperty);
            }
            set
            {
                SetValue(HeaderTemplateProperty, value);

            }
        }

        public Thickness ContentPadding
        {
            get
            {
                return (Thickness)GetValue(ContentPaddingProperty);
            }
            set
            {
                SetValue(ContentPaddingProperty, value);

            }
        }
        #endregion

        #region Dependency
        public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register("IsExpanded", typeof(bool), typeof(MenuItem), new PropertyMetadata(null));
        public static readonly DependencyProperty ContextMenuVisibilityProperty = DependencyProperty.Register("ContextMenuVisibility", typeof(Visibility), typeof(MenuItem), new PropertyMetadata(null));
        public static readonly DependencyProperty ArrowVisibilityProperty = DependencyProperty.Register("ArrowVisibility", typeof(Visibility), typeof(MenuItem), new PropertyMetadata(null));
        public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register("Header", typeof(object), typeof(MenuItem), new PropertyMetadata(null));
        public static readonly DependencyProperty HeaderTemplateProperty = DependencyProperty.Register("HeaderTemplate", typeof(DataTemplate), typeof(MenuItem), new PropertyMetadata(null));
        public static readonly DependencyProperty ArrowContainerWidthProperty = DependencyProperty.Register("ArrowContainerWidth", typeof(double), typeof(MenuItem), new PropertyMetadata(null));
        public static readonly DependencyProperty PopupOffsetProperty = DependencyProperty.Register("PopupOffsetTemplate", typeof(double), typeof(MenuItem), new PropertyMetadata(null));
        public static readonly DependencyProperty ContentPaddingProperty = DependencyProperty.Register("ContentPadding", typeof(Thickness), typeof(MenuItem), new PropertyMetadata(null));
        #endregion

        #region Methods
        private void handlePopupVisiblity()
        {
            if (this.IsMainMenuItem)
            {
                if (this.ParentMenu != null)
                {
                    //Hide all but this main panel
                    foreach (UIElement element in this.ParentMenu.Items)
                    {
                        if (element is MenuItem && element != this.Parent)
                        {
                            MenuItem mainMenuItem = element as MenuItem;
                            if (mainMenuItem.IsExpanded && mainMenuItem != this)
                            {
                                mainMenuItem.IsExpanded = false;
                            }
                        }
                    }
                }
            }
            else
            {
                if (this.ParentMenuItem != null)
                {
                    //Ensure that only one panel is opened on the current level
                    foreach (UIElement element in this.ParentMenuItem.Items)
                    {
                        if (element is MenuItem && element != this.Parent)
                        {
                            MenuItem menuItem = element as MenuItem;
                            if (menuItem.IsExpanded && menuItem.HasChild)
                            {
                                menuItem.IsExpanded = false;
                            }
                        }
                    }
                }
            }

            if (this.HasChild)
            {
                //Toggle panel
                this.IsExpanded = true;
            }
            else if (this.ParentMainMenuItem != null)
            {
                //Hide panel on click
                this.ParentMainMenuItem.IsExpanded = false;
            }
        }

        protected FrameworkElement FindMainMenuItem(FrameworkElement element)
        {
            FrameworkElement result = null;
            if (element != null && element.Parent != null)
            {
                if (element is MenuItem && ((MenuItem)element).IsMainMenuItem)
                {
                    return element;
                }
                else if (element.Parent is MenuItem && ((MenuItem)element.Parent).IsMainMenuItem)
                {
                    return element.Parent as FrameworkElement;
                }
                else if (element.Parent is FrameworkElement)
                {
                    return FindMainMenuItem(element.Parent as FrameworkElement);
                }
            }
            return result;
        }
        #endregion

        #region Template
        protected FrameworkElement RootElement;
        protected FrameworkElement Arrow;
        protected StackPanel Container;
        protected StackPanel ItemsContainer;
        protected Popup popup;
        protected MenuItemHeader header;

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            RootElement = (FrameworkElement)GetTemplateChild("RootElement");
            ItemsContainer = (StackPanel)GetTemplateChild("ItemsContainer");
            Arrow = (FrameworkElement)GetTemplateChild("Arrow");
            popup = (Popup)GetTemplateChild("Popup");
            header = (MenuItemHeader)GetTemplateChild("Header");

            header.Click += new RoutedEventHandler(Button_Click);

            refreshLayout();
        }

        private void refreshLayout()
        {
            if (header != null)
            {
                header.IsMainMenuItem = this.IsMainMenuItem;
            }

            if (this.IsMainMenuItem)
            {
                this.ContentPadding = new Thickness(6, 0, 6, 0);
                this.ArrowVisibility = Visibility.Collapsed;
                this.ArrowContainerWidth = 0;
                //header.ArrowContainerWidth = 0;
            }
            else
            {
                this.ContentPadding = new Thickness(10, 2, 10, 2);
                this.ArrowVisibility = (this.HasChild ? Visibility.Visible : Visibility.Collapsed);
                this.ArrowContainerWidth = 16;
                //header.ArrowContainerWidth = 16;

                if (popup != null)
                {
                    //popup.UseLayoutRounding = true;
                    //popup.VerticalOffset = 0;
                    //popup.HorizontalOffset = 40;
                    popup.Margin = new Thickness(this.ActualWidth - 3, -22, 0, 0);
                }
            }
        }
        #endregion

        #region Events
        protected override void OnMouseEnter(MouseEventArgs e)
        {
            if (this.ParentMenu != null && this.ParentMenu.IsNavigating)
            {
                if (this.IsMainMenuItem)
                {
                    handlePopupVisiblity();
                }
                else
                {
                    if (this.ParentMenuItem != null)
                    {
                        this.IsExpanded = this.HasChild;

                        //Ensure that only one panel is opened on the current level
                        foreach (UIElement element in this.ParentMenuItem.Items)
                        {
                            if (element is MenuItem && element != this)
                            {
                                MenuItem menuItem = element as MenuItem;
                                if (menuItem.IsExpanded && menuItem.HasChild)
                                {
                                    menuItem.IsExpanded = false;
                                }
                            }
                        }
                    }
                }
            }
            VisualStateManager.GoToState(header, "MouseOver", true);
            base.OnMouseEnter(e);
        }

        protected override void OnMouseLeave(MouseEventArgs e)
        {
            base.OnMouseLeave(e);
        }

        protected void MenuItem_LayoutUpdated(object sender, EventArgs e)
        {
            //Arrow is shown only when MenuItem has subitems and does not appear in the Menu bar
            this.ArrowVisibility = (this.HasChild && !this.IsMainMenuItem ? Visibility.Visible : Visibility.Collapsed);

            refreshLayout();
        }

        protected void Button_Click(object sender, RoutedEventArgs e)
        {
            handlePopupVisiblity();
            if (this.ParentMenu != null)
            {
                //If main menu item has been clicked (to expand)
                if (this.IsMainMenuItem && this.IsExpanded && !this.ParentMenu.IsNavigating)
                {
                    this.ParentMenu.IsNavigating = true;
                }
                if (!this.IsMainMenuItem && !this.HasChild)
                {
                    this.ParentMenu.IsNavigating = false;
                }
                this.ParentMenu.OnItemClick(new MenuEventArgs(this));
            }

            OnClick(e);
        }
        #endregion

        #region Bubble events
        public event RoutedEventHandler Click;
        protected virtual void OnClick(RoutedEventArgs e)
        {
            if (Click != null)
            {
                Click(this, e);
            }
        }
        #endregion
    }
}
